#!/bin/sh

# @configure_input@

# Script to setup chroot environment extras needed to perform a juding run.
#
# This script will (bind) mount a minimal set of subdirectories from a
# pre-built chroot tree into the actual chroot environment that is
# created for each judging (as well as the proc FS and a few device
# files). Afterwards it is also called to cleanup.
#
# You can adapt this script to your environment, e.g. if you need to make
# more/other subdirectories available in the chroot environment.
#
# See bin/dj_make_chroot.sh for a script to generate the
# chroot environment. Note that if you modify paths
# in this script, then the associated sudo rules (see
# etc/sudoers-domjudge) must also be updated.
#
# This script will be called from judgedaemon.main.php in the root
# directory of the chroot environment with one parameter, either:
# - 'check' to perform some sanity checks;
# - 'start' to setup; or
# - 'stop' to destroy the chroot environment.
#
# We always use 'sudo -n <command> < /dev/null' to make sure that sudo
# doesn't try to ask for a password, but just fails.

# Exit on error:
set -e

# Chroot subdirs needed: (optional lib64 only needed for amd64 architecture)
SUBDIRMOUNTS="etc usr lib lib64 bin"

# Location of the pre-built chroot tree and where to bind mount from:
CHROOTORIGINAL="@judgehost_chrootdir@"

dj_umount() {
	set +e
	if ! sudo -n umount "$1" < /dev/null; then
		>&2 echo "umount '$1' didn't succeed, trying harder"
		if ! sudo -n umount -f -vvv "$1" < /dev/null; then
			>&2 echo "umount '$1' failed twice"
			>&2 lsof +c 15 +D "$1"
			exit 1
		fi
	fi
	set -e
}

case "$1" in
	check)
		if [ ! -d "$CHROOTORIGINAL" ]; then
			>&2 echo "chroot dir '$CHROOTORIGINAL' does not exist, run dj_make_chroot"
			exit 2
		fi
		for i in $SUBDIRMOUNTS ; do
			if [ ! -e "$CHROOTORIGINAL/$i" ]; then
				>&2 echo "chroot subdir '$CHROOTORIGINAL/$i' not found, rerun dj_make_chroot"
				exit 2
			fi
		done
		# This directory is removed at the very end of the debootstrap run, if it's
		# still present, building the chroot did not complete.
		if [ -d "$CHROOTORIGINAL/debootstrap" ]; then
			>&2 echo "chroot dir '$CHROOTORIGINAL' incomplete, rerun dj_make_chroot"
			exit 2
		fi
		;;
	start)

		# Mount (bind) the proc filesystem (needed by Java for /proc/self/stat):
		mkdir -p proc
		sudo -n mount -n --bind /proc proc < /dev/null

		# shellcheck disable=SC2086
		for i in $SUBDIRMOUNTS ; do

			# Some dirs may be links to others, e.g. /lib64 -> /lib.
			# Preserve those; bind mount the others.
			if [ -L "$CHROOTORIGINAL/$i" ]; then
				ln -s "$(readlink "$CHROOTORIGINAL/$i")" "$i"
			elif [ -d "$CHROOTORIGINAL/$i" ]; then
				mkdir -p $i
				sudo -n mount --bind "$CHROOTORIGINAL/$i" "$i" < /dev/null
				# Mount read-only for extra security. Note that this
				# must be executed separately from the bind mount.
				sudo -n mount -o remount,ro,bind "$PWD/$i" < /dev/null
			fi
		done

		# copy dev/random and /dev/urandom as a random source
		mkdir -p dev
		sudo -n cp -pR /dev/random  dev < /dev/null
		sudo -n cp -pR /dev/urandom dev < /dev/null
		sudo -n chmod o-w dev/random dev/urandom < /dev/null
		;;

	stop)

		dj_umount "$PWD/proc"

		rm -f dev/urandom
		rm -f dev/random
		rmdir dev || true

		for i in $SUBDIRMOUNTS ; do
			if [ -L "$CHROOTORIGINAL/$i" ]; then
				rm -f $i
			elif [ -d "$CHROOTORIGINAL/$i" ]; then
				dj_umount "$PWD/$i"
			fi
		done
# KLUDGE: We don't rmdir the empty mountpoint directories, since after
# unmounting, we sometimes still get error messages "Device or
# resource busy" when trying to. This seems only to occur when
# multiple judgedaemons are run on a single host...
		;;

	*)
		echo "Unknown argument '$1' given."
		exit 1
esac

exit 0
